package ru.amse.baltijsky.javascheme.exporter.image;

import ru.amse.baltijsky.javascheme.node.AbstractNode;
import ru.amse.baltijsky.javascheme.nodeshape.INodeShape;
import ru.amse.baltijsky.javascheme.nodeshape.NoShape;
import ru.amse.baltijsky.javascheme.tree.SchemaNode;
import ru.amse.baltijsky.javascheme.util.Size;

/**
 * The root of the image nodes hierarchy that mirrors the structure of the schema tree, but contains information
 * about the graphic representation of the node.
 */
public class VisualNode extends AbstractNode<VisualNode> {
    /**
     * The schema node that is visualized by this node.
     */
    private SchemaNode schemaNode = null;
    /**
     * The shape strategy of the node that knows how to draw it properly.
     */
    private INodeShape shape = new NoShape();
    /**
     * Closing shape. Makes sense for nesting nodes: it is drawn in the point where the nested level ends, in the
     * point where children lines meet the node level line.
     */
    private INodeShape closingShape = new NoShape();
    /**
     * The size of the visual tree with the root in this node (ie., all nodes in the visual tree reachable from this
     * if we restrict to <tt>next</tt> and <tt>children</tt> links).
     */
    private Size subtreeSize = new Size();
    /**
     * The size of the left part of the subtree (part of the subtree lying to the left of this node's vertical guide line).
     */
    private int leftSubtreeSize = 0;
    /**
     * Offset for the node inside its subtree bounding rect.
     */
    private Size offset = new Size();
    /**
     * The size of the subtree containing nodes reachable from this node and its children.
     */
    private Size childrenSubtreeSize = new Size();

    /**
     * The size of the subtree consisting of all nodes reachable from this <b>only</b> by <tt>.next</tt> links.
     */
    private Size nextSubtreeSize = new Size();

    /**
     * Creates a new visual node with the specified tree topology.
     *
     * @param parentNode the parent in the visual nodes tree
     * @param prevNode   the previous node (such node N that N.next equals to this)
     * @param schemaNode the schema node that this visual node corresponds to
     */
    public VisualNode(VisualNode parentNode, VisualNode prevNode, SchemaNode schemaNode) {
        this(parentNode, prevNode, schemaNode.getShape(), schemaNode.getClosingShape());
        this.schemaNode = schemaNode;
    }

    public VisualNode(VisualNode parent, VisualNode prev, INodeShape theShape) {
        this(parent, prev, theShape, new NoShape());
    }

    public VisualNode(VisualNode parent, VisualNode prev, INodeShape theShape, INodeShape theClosingShape) {
        setParent(parent);
        if (prev != null) {
            prev.setNext(this);
        }
        shape = theShape;
        closingShape = theClosingShape;
    }

    /**
     * Getter for the shape of the node.
     *
     * @return node shape
     */
    public INodeShape getShape() {
        return shape;
    }

    /**
     * Getter for the closing shape.
     *
     * @return the closing shape
     */
    public INodeShape getClosingShape() {
        return closingShape;
    }

    public Size getSubtreeSize() {
        return subtreeSize;
    }

    public void setSubtreeSize(int leftSubtreeSize, int rightSubtreeSize, int subtreeSizeY) {
        this.subtreeSize = new Size(leftSubtreeSize + rightSubtreeSize, subtreeSizeY);
        this.leftSubtreeSize = leftSubtreeSize;
    }

    public int getLeftSubtreeSize() {
        return leftSubtreeSize;
    }

    public int getRightSubtreeSize() {
        return subtreeSize.x - leftSubtreeSize;
    }

    public Size getChildrenSubtreeSize() {
        return childrenSubtreeSize;
    }

    public void setChildrenSubtreeSize(Size childrenSubtreeSize) {
        this.childrenSubtreeSize = childrenSubtreeSize;
    }

    public Size getOffset() {
        return offset;
    }

    public void setOffset(Size offset) {
        this.offset = offset;
    }

    public Size getNextSubtreeSize() {
        return nextSubtreeSize;
    }

    public void setNextSubtreeSize(Size nextSubtreeSize) {
        this.nextSubtreeSize = nextSubtreeSize;
    }

    public SchemaNode getSchemaNode() {
        return schemaNode;
    }
}
